import datetime

import pandas as pd

from db.session import get_collection
from model import evaluate
from model.evaluate import Evaluate
from model.trades import TradeHistory, OrderHistory
from service import dataService
from service.strategyService.BaseStrategyServiceModel import BaseStrategyServiceModel
from utils import utils


class Broker:
    """
    帐户
    """
    def __init__(self):
        self.__cash = 10000
        self.__position = 0 # 持仓
        self.__commission_rate = 0.003 # 佣金比例
        self.__closing_fee_rate = 0.00001 # 过户费比例
        self.__stamp_duty_rate = 0.001 # 印花税比例
        self.__hands = 1

    def setCash(self, cash):
        self.__cash = cash

    def setCommission(self, commission_rate):
        self.__commission_rate = commission_rate

    def setHands(self, hands):
        self.__hands = hands

    def buy(self, val):
        position = self.__hands * 100 # 仓数
        val = val * position # 买入的金额
        commission = max(self.__commission_rate * val, 5) # 佣金
        closing_free = self.__closing_fee_rate * val # 过户费
        profit_cash = -(val + commission + closing_free) # 资金的变换
        self.__cash += profit_cash
        self.__position += position
        return profit_cash, self.__position

    def sell(self, val):
        # 把仓位全部卖出
        val = val * self.__position
        commission = max(self.__commission_rate * val, 5) # 佣金
        closing_free = self.__closing_fee_rate * val # 过户费
        stamp_duty = self.__stamp_duty_rate * val # 印花税
        profit_cash = val - (commission + closing_free + stamp_duty)
        self.__cash += profit_cash
        self.__position = 0 # 清仓
        return profit_cash, self.__position

    def getValue(self):
        return self.__cash

    def getPosition(self):
        return self.__position

class BackTest:
    """
    回测
    """
    def __init__(self):
        self.broker = Broker()
        self.__data = pd.DataFrame()
        self.__result = pd.DataFrame()
        self.__strategy = BaseStrategyServiceModel()
        self.evaluate = Evaluate(
            profit_rate=0
        )
        self.orderID = utils.generate_order_id()

    def setData(self, data):
        self.__data = data

    def setStrategy(self, strategy):
        self.__strategy = strategy

    def getResult(self):
        return self.__result

    async def trade(self):
        """
        包含交易信号 'signal', 收盘价即最新价 'close'
        :return:
        """
        # 初始资本
        initCash = self.broker.getValue()
        for index ,row in self.__result.iterrows():
            # 设计参考指标
            # 买入
            if row['signal'] == 1:
                profit_cash, new_position = self.broker.buy(row['close'])
            elif row['signal'] == -1:
                if self.broker.getPosition() > 0:
                    profit_cash, new_position = self.broker.sell(row['close'])
                else:
                    profit_cash = 0
                    new_position = 0
            else:
                profit_cash = 0
                new_position = 0
            tradeHistory = TradeHistory(
                orderID=self.orderID,
                date_time=index,
                price=row['close'],
                signal=row['signal'],
                profit_cash=profit_cash,
                cash=self.broker.getValue(),
                new_position=new_position,
                total_position=self.broker.getPosition()
            )
            await dataService.insert_tradeHistory(tradeHistory)
        # 计算指标
        # 收益率
        self.evaluate = Evaluate(
            profit_rate=Evaluate.cal_profit_rate(initCash, self.broker.getValue())
        )

    async def run(self):
        # 生成交易信号
        result_ls = []
        for _, row in self.__data.iterrows():
            result = await self.__strategy.apply(row)
            result_ls.append(result)
        self.__result = pd.DataFrame(result_ls)
        # 保存股票代码
        code = self.__data.iloc[0]['code']
        # 进行交易
        await self.trade()
        # 交易完后进行保存
        # 返回订单记录和评估指标
        orderHistory = OrderHistory(orderID=self.orderID,
                                    code=code,
                                    date_time=datetime.datetime.now(),
                                    strategy_name=self.__strategy.getStrategyName(),
                                    evaluate=self.evaluate)
        await dataService.insert_orderHistory(orderHistory)
        return orderHistory, self.evaluate